﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NLog;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

namespace EntlibCacheStorage
{
    public class CacheImpl : ICache
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        [NonSerialized]
        private readonly ICacheManager _cacheManager;

        private readonly ICacheItemRefreshAction _cacheItemRefreshAction;

        #region CTOR
        public CacheImpl(string cacheManagerName)
            : this(CacheFactory.GetCacheManager())
        {
            logger.Info("CacheImplement @ " + cacheManagerName);

            _cacheItemRefreshAction = new CacheItemRefreshAction(_cacheManager);
        }
        private CacheImpl(ICacheManager cacheManager)
        {
            if (cacheManager == null)
                throw new ArgumentNullException("cacheManager");

            _cacheManager = cacheManager;
        }
        #endregion

        #region ICache
        public int Count
        {
            get { return _cacheManager.Count; }
        }

        public void Clear()
        {
            _cacheManager.Flush();
        }

        public bool Contains(string key)
        {
            return _cacheManager.Contains(key);
        }

        public T Get<T>(string key)
        {
            if (Contains(key))
                return (T)_cacheManager.GetData(key);
            else
                return default(T);
        }

        public bool TryGet<T>(string key, out T value)
        {
            value = default(T);

            if (_cacheManager.Contains(key))
            {
                object existingValue = _cacheManager.GetData(key);

                if (existingValue != null)
                {
                    value = (T)existingValue;

                    return true;
                }
            }

            return false;
        }

        public void Set<T>(string key, T value)
        {
            Set<T>(key, value, DateTime.Now.Add(new TimeSpan(0, 1, 0)));
        }

        public void Set<T>(string key, T value, DateTime absoluteExpiration)
        {
            try
            {
                RemoveIfExists(key);
                _cacheManager.Add(key, value, CacheItemPriority.Normal, _cacheItemRefreshAction, new AbsoluteTime(absoluteExpiration.ToLocalTime()));
            }
            catch (Exception ex)
            {
                logger.Error("Set key: " + key, ex);
            }
        }

        public void Set<T>(string key, T value, TimeSpan slidingExpiration)
        {
            DateTime absoTime = DateTime.Now.Add(slidingExpiration);
            Set<T>(key, value, absoTime);
        }

        public void Remove(string key)
        {
            _cacheManager.Remove(key);
        }
        #endregion

        #region Helper Method
        internal void RemoveIfExists(string key)
        {
            if (_cacheManager.Contains(key))
            {
                _cacheManager.Remove(key);
            }
        }
        #endregion
    }
}
